﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;
using TestByWire.AttributeHandlers;

namespace TestByWire.Test
{
	[TestClass]
	public class UseFakeLateBoundInteractionTest
	{
		[TestMethod]
		public void Constructor_ShouldThrow_WhenFieldNameIsNull()
		{
			var exception = AssertEx.Throws<ArgumentNullException>(() => new UseFakeLateBoundInteraction(default(string), default(int)));

			Assert.AreEqual("registrationName", exception.ParamName);
		}

		[TestMethod]
		public void Constructor_ShouldAssignRegistrationName_WhenInitialized()
		{
			var interaction = new UseFakeLateBoundInteraction("_theFieldName", default(int));

			Assert.AreEqual("_theFieldName", interaction.RegistrationName);
		}

		[TestMethod]
		public void Constructor_ShouldAssignRegistrationType_WhenInitialized()
		{
			var interaction = new UseFakeLateBoundInteraction("_theFieldName", default(int));

			Assert.AreEqual(typeof(int), interaction.RegistrationType);
		}

		[TestMethod]
		public void Constructor_ShouldThrow_WhenFakeInstanceIsNull()
		{
			var exception = AssertEx.Throws<ArgumentNullException>(() => new UseFakeLateBoundInteraction("Test", null));

			Assert.AreEqual("fakeInstance", exception.ParamName);
		}

		[TestMethod]
		public void Constructor_ShouldAssignFakeInstance_WhenInitialized()
		{
			var interaction = new UseFakeLateBoundInteraction("_theFieldName", 10);

			Assert.AreEqual(10, interaction.FakeInstance);
		}

		[TestMethod]
		public void Execute_ShouldApplyFakeToField_WhenTestTargetIsSet()
		{
			var testTarget = new FakeTestTarget();
			var fakeInteraction = new UseFakeLateBoundInteraction("_theValue", 20);
			var buildContext = MockRepository.GenerateStub<IBuildContext>();
			buildContext.Stub(b => b.TestTarget).Return(testTarget);

			fakeInteraction.Execute(buildContext);

			Assert.AreEqual(20, testTarget.GetValue());
		}

		[TestMethod]
		public void Execute_ShouldOptOut_WhenTestTargetIsNotSet()
		{
			var fakeInteraction = new UseFakeLateBoundInteraction("_theValue", 20);
			var buildContext = MockRepository.GenerateStub<IBuildContext>();

			fakeInteraction.Execute(buildContext);
		}

		[TestMethod]
		public void Execute_ShouldThrow_WhenFieldNameIsNotValid()
		{
			var testTarget = new FakeTestTarget();
			var fakeInteraction = new UseFakeLateBoundInteraction("_theUnknownValue", 20);
			var buildContext = MockRepository.GenerateStub<IBuildContext>();
			buildContext.Stub(b => b.TestTarget).Return(testTarget);
			
			var exception = AssertEx.Throws<InvalidOperationException>(() => fakeInteraction.Execute(buildContext));

			Assert.AreEqual("Field: _theUnknownValue, does not exist on Test Target: TestByWire.Test.UseFakeLateBoundInteractionTest+FakeTestTarget", exception.Message);
		}

		private class FakeTestTarget
		{
#pragma warning disable 649
			private int _theValue;
#pragma warning restore 649

			public int GetValue()
			{
				return _theValue;
			}
		}
	}
}
